﻿// <copyright file="SplintOutputParser.cs" company="Horiba Medical">
//     Copyright (c) Horiba Medical. All rights reserved.
// </copyright>
// <summary>
//     Splint output parser.
// </summary>

namespace DoxyOutParser.Parser
{
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.Globalization;
	using System.IO;
	using System.Windows.Forms;
	using DoxyOutParser.Forms;

	/// <summary>
	/// Provide the functions and methods enabling the parsing of a warning log generated by Splint.
	/// </summary>
	public class SplintOutputParser : IOutputParser
	{
		/// <summary>
		/// Parses a splint error log for entries.
		/// </summary>
		/// <param name="errorLog">Path and name of the file to parse.</param>
		public static void ParseAndDisplayErrors(string errorLog)
		{
			try
			{
				StreamReader errorStream = new StreamReader(errorLog);
				string errorLine = errorStream.ReadToEnd();

				if (errorLine.Length != 0)
				{
					//InfoForm infoBox = new InfoForm("Splint returned the following error:\n" + errorLine, "Error while parsing ...");
					// infoBox.Show();
					MessageBox.Show("Splint returned the following error:\n" + errorLine, "Error while parsing ...");
				}
			}
			catch (DirectoryNotFoundException)
			{
				throw;
			}
		}

		/// <summary>
		/// Parses a warning log for entries.
		/// </summary>
		/// <param name="outputFile">Path and name of the file to parse.</param>
		/// <param name="filter">Filter to apply on the displayed items.</param>
		/// <returns>A list of output entries found in the log file.</returns>
		public OutputEntryCollection Parse(string outputFile, string filter)
		{
			OutputEntryCollection entryList = new OutputEntryCollection();
			try
			{
				// Using of StreamReader to parse the file
				StreamReader cycleStreamReader = new StreamReader(outputFile);
				string line = cycleStreamReader.ReadLine();
				string[] paramSeparators = { ":" };
				OutputEntry newEntry = null;


				while (line != null)
				{
					if (line.Length == 0)
					{
						// Skip of empty line
						line = cycleStreamReader.ReadLine();
						if (line.Contains(".\\lib\\STM32F10x_StdPeriph_Driver\\src\\stm32f10x_flash.c"))
						{
							Console.WriteLine("Attention");
						}
					}
					else
					{
						// Non empty line 
						// Look at next line
						string nextLine = cycleStreamReader.ReadLine();
						if (line.StartsWith("Command Line", StringComparison.Ordinal))
						{
							// Splint executable warning found
							string[] entryData = line.Split(paramSeparators, 2, StringSplitOptions.RemoveEmptyEntries);
							string details = string.Empty;

							// Concatenation of all following line linked to the first
							while (nextLine.StartsWith(" ", StringComparison.Ordinal))
							{
								details += " " + nextLine.TrimStart(' ');
								nextLine = cycleStreamReader.ReadLine();
							}

							// Create an entry for this warning.
							newEntry = CreateSplintOutputEntryFromCommandLineData(entryData, details);
						}
						else
						{
							// All warning linked to checked code
							string[] entryData =  line.Split(paramSeparators, 4, StringSplitOptions.RemoveEmptyEntries);
							string details = string.Empty;
							string type = string.Empty;

							// Check the number of argument in the warning header
							if (entryData.Length == 2)
							{
								if (entryData[1].Contains("(in function")==false)
								{
									// "In function" lines
									try
									{
										do
										{
											nextLine = cycleStreamReader.ReadLine();
											if (nextLine != null)
											{
												if (nextLine.Length != 0)
												{
													if (nextLine.StartsWith("."))
													{
														break;
													}
													else
													{
														line += nextLine;
													}
												}
											}
											else
											{
												break;
											}
										}
										while (nextLine.StartsWith(".") == false);

										////newEntry = CreateSplintOutputEntryFromSeparator(entryData);
									}
									catch (NullReferenceException)
									{
										// Do nothing. (We found the end of file during the processing.
									}
								}
							}
							else
							{
								// Regular code warning line
								try
								{
									if (entryData.Length == 4)
									{
										// The "warning" was in the line
										type = entryData[3];
									}
									else
									{
										// The "warning" is on the other line
										type = nextLine;
										nextLine = cycleStreamReader.ReadLine();
									}

									// Concat all the line starting with "..." which are the following of the first warning line
									while (nextLine.StartsWith("   ", StringComparison.Ordinal))
									{
										type += " " + nextLine.TrimStart(' ');
										nextLine = cycleStreamReader.ReadLine();
									}

									// Concate everything that follows ( warning detail description )
									while (nextLine.StartsWith(" ", StringComparison.Ordinal))
									{
										details += " " + nextLine.TrimStart(' ');
										nextLine = cycleStreamReader.ReadLine();
									}
								}
								catch (NullReferenceException)
								{
									// Do nothing. (We found the end of file during the processing.
								}
								finally
								{
									newEntry = CreateSplintOutputEntryFromEntryData(entryData, type, details);
								}
							}
						}
						if (newEntry != null)
						{
							if (filter.Length != 0)
							{
								if (newEntry.FileNameAndPath.ToString().Contains(filter))
								{
									entryList.Add(newEntry);
									newEntry = null;
								}
							}
							else
							{
								entryList.Add(newEntry);
								newEntry = null;
							}
						}
						line = nextLine;
					}
				}
				cycleStreamReader.Close();
				return entryList;
			}
			catch (Exception e)
			{
				throw e;
			}
		}

		/// <summary>
		/// Creates a Splint OutputEntry object from a splint warning log command-line entry.
		/// </summary>
		/// <param name="commandLineData">Command line entry.</param>
		/// <param name="messageDetails">Details from the command-line warning.</param>
		/// <returns>The created output entry.</returns>
		private static OutputEntry CreateSplintOutputEntryFromCommandLineData(string[] commandLineData, string messageDetails)
		{
			OutputEntry newEntry = new OutputEntry();

			if (commandLineData.Length == 2)
			{
				newEntry.FileNameAndPath = commandLineData[0];
				newEntry.Line = "-";
				newEntry.MessageType = commandLineData[1].TrimStart(' ', '\r', '\n');
				newEntry.MessageDetails = messageDetails.TrimStart(' ', '\r', '\n');
			}
			else
			{
				throw new ArgumentOutOfRangeException("commandLineData", "The input string for this method should be composed of 2 strings.");
			}

			return newEntry;
		}

		/// <summary>
		/// Creates a Splint OutputEntry object from a warning log entry.
		/// </summary>
		/// <param name="entryData">The entry to parse.</param>
		/// <param name="type">Type for this warning.</param>
		/// <param name="details">Details from this warning.</param>
		/// <returns>The created output entry.</returns>
		private static OutputEntry CreateSplintOutputEntryFromEntryData(string[] entryData, string type, string details)
		{
			OutputEntry newEntry = new OutputEntry();

			if (entryData.Length >= 3)
			{
				newEntry.FileNameAndPath = entryData[0];

				newEntry.Line = entryData[1];

				// Column information comes third on a Splint log, some modifications are needed on the OutputEntry class to allow this
				////newEntry.Column = entryData[2]
				newEntry.MessageType = type.TrimStart(' ', '\r', '\n');

				newEntry.MessageDetails = details.TrimStart(' ', '\r', '\n');
			}
			else
			{
				throw new ArgumentOutOfRangeException("entryData", "The input string for this method should be composed of a minimum of 3 strings.");
			}

			return newEntry;
		}

		/// <summary>
		/// Creates a Splint OutputEntry object from a separator entry.
		/// </summary>
		/// <param name="separator">The entry to parse.</param>
		/// <returns>The created output entry.</returns>
		private static OutputEntry CreateSplintOutputEntryFromSeparator(string[] separator)
		{
			OutputEntry newEntry = new OutputEntry();

			if (separator.Length == 2)
			{
				newEntry.FileNameAndPath = separator[0];

				newEntry.Line = "-";

				newEntry.MessageType = separator[1].TrimStart(' ', '\r', '\n');

				newEntry.MessageDetails = "-";
			}
			else
			{
				throw new ArgumentOutOfRangeException("separator", "The input string for this method should be composed of a minimum of 2 strings.");
			}

			return newEntry;
		}
	}
}
